package cn.zhaopin.starter.security.manager;

import cn.zhaopin.common.constant.LogConstants;
import cn.zhaopin.common.context.UserContextHolder;
import cn.zhaopin.starter.security.basic.CodeException;
import cn.zhaopin.starter.security.basic.UserContext;
import cn.zhaopin.starter.security.common.SecurityConstant;
import cn.zhaopin.starter.security.service.SecurityUserDetailService;
import cn.zhaopin.starter.security.token.WeWorkThirdAuthenticationToken;
import cn.zhaopin.starter.security.user.SecurityContextHolderBuilder;
import cn.zhaopin.starter.security.wechat.WeWork3rdUserInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import reactor.core.publisher.Mono;

import java.util.Objects;

/**
 * Description: 企微内部登陆
 *
 * @author zuomin (myleszelic@outlook.com)
 * @date: 2021/09/28-19:12
 */
@Slf4j
public class WeWorkInsideAuthenticationManager implements ReactiveAuthenticationManager {

    public final SecurityUserDetailService securityUserDetailService;
    public final ObjectMapper objectMapper;

    public WeWorkInsideAuthenticationManager(SecurityUserDetailService securityUserDetailService) {
        this.securityUserDetailService = securityUserDetailService;
        objectMapper = new ObjectMapper();
    }

    @SuppressWarnings("BlockingMethodInNonBlockingContext")
    @SneakyThrows
    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        WeWorkThirdAuthenticationToken authenticationToken = (WeWorkThirdAuthenticationToken) authentication;
        String codeReq = authenticationToken.getCredentials().toString();
        String loginType = authenticationToken.getLoginType();

        //获取企业微信用户信息
        WeWork3rdUserInfo userInfo = securityUserDetailService.get3rdUserInfo(codeReq, loginType);
        log.info("code:{}, loginType:{}, userInfo:{}", codeReq, loginType, userInfo);
        if (Objects.isNull(userInfo)) {
            throw CodeException.builder().code(SecurityConstant.NO_PERMISSION_ERROR_CODE).msg("获取访问身份信息为空").build();
        }

        // 校验是否有权限(条件: expireIn==101 特定条件:需要与)
        if (SecurityConstant.NO_PERMISSION_EXPIRE_IN.equals(userInfo.getExpiresIn())) {
            log.info("40007特定情况 用户信息:[{}]", objectMapper.writeValueAsString(userInfo));
            throw CodeException.builder().code(SecurityConstant.NO_PERMISSION_ERROR_CODE).msg("用户不在应用可见范围").build();
        }

        //调用自定义的userDetailsService认证
        UserContext user = securityUserDetailService.findUserByWeWork3rdUserId(userInfo.getCorpId(), userInfo.getUserId());
        log.info("UserDetails:{}", user);
        if (user == null) {
            throw CodeException.builder().code(SecurityConstant.NO_PERMISSION_ERROR_CODE).msg("无法获取用户信息").build();
        }
        MDC.put(LogConstants.LOG_USER_ID, user.getId().toString());
        UserContextHolder.setUserId(user.getId().toString());

        //保存用户信息
        Boolean save3rdUserDetail = securityUserDetailService.save3rdUserDetail(userInfo.getUserTicket());
        if (!save3rdUserDetail) {
            throw CodeException.builder().code(SecurityConstant.NO_PERMISSION_ERROR_CODE).msg("保存身份敏感信息失败").build();
        }
        //已认证
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user, authentication.getCredentials(), user.getAuthorities());

        //设置securityContextHolder
        SecurityContextHolderBuilder.build(token);

        return Mono.just(token);
    }
}
